﻿-------------------------------------------------------------------------------
-- Addon - Combo-Points - Init
-------------------------------------------------------------------------------
-- Version
local MAJOR = "Avion2_ComboPoints"
local MINOR = tonumber(("@project-revision@"):match("%d+")) or 0

-- Dependencies
if not Avion2Core 				then error(MAJOR .. " requires Avion2.") end
if not LibStub("AceEvent-3.0")	then error(MAJOR .. " requires AceEvent-3.0.") end

-- Create
Avion2_ComboPoints 			= Avion2Core:NewModule(MAJOR, "AceEvent-3.0")
Avion2_ComboPoints.major 	= MAJOR
Avion2_ComboPoints.minor 	= MINOR


-------------------------------------------------------------------------------
-- Addon - Combo-Points - Variables
-------------------------------------------------------------------------------
-- Translation
local L 	= LibStub("AceLocale-3.0"):GetLocale(MAJOR)
local coreL	= LibStub("AceLocale-3.0"):GetLocale(Avion2Core.major)

-- Local functions
local UpdateEnergy, UpdateComboPoints


-------------------------------------------------------------------------------
-- Addon - Combo-Points - Register with core
-------------------------------------------------------------------------------
-- Register SavedVariables
function Avion2_ComboPoints:RegisterModuleDB()
	-- Register own saved variables DB
	self.db = Avion2Core.db:RegisterNamespace(Avion2_ComboPoints.major, {
		profile = {
			energy 		= {
				data 		= {},
				onlyHighest = false,
			},
			comboPoints	= {
				data 		= {},
				onlyHighest = false,
			},
			lastMinor 	= MINOR,
		},
	})
	
	-- Last combo-points
	self.lastComboPoints = 0
	
	-- Track last energy value
	self.lastUnitPower = 0
	
	-- Updateto to new rev, data converters should go here
	self.db.profile.lastMinor = Avion2_ComboPoints.minor
end


-- (Un)register (un)needed events
function Avion2_ComboPoints:UpdateEventRegistration()
	local enabled = false
	for key, value in pairs(self.db.profile.energy.data) do
		if (#value.animationList > 0) then
			enabled = true
		end
	end
	
	-- Energy events
	if (self:IsEnabled() and enabled) then
		self:RegisterEvent("UNIT_ENERGY", UpdateEnergy)
	else
		self:UnregisterEvent("UNIT_ENERGY")
	end
	
	enabled = false
	for key, value in pairs(self.db.profile.comboPoints.data) do
		if (#value.animationList > 0) then
			enabled = true
		end
	end
	
	-- Combo-Points  events
	if (self:IsEnabled() and enabled) then
		self:RegisterEvent("PLAYER_COMBO_POINTS", UpdateComboPoints)
	else
		self:UnregisterEvent("PLAYER_COMBO_POINTS")
	end
end


-- Update animation (convert animationFunc: string name to function pointer)
function Avion2_ComboPoints:LoadAnimationTables()
	for _, data in pairs(self.db.profile.energy.data) do
		Avion2Core:LoadAnimationTables(data.animationList)
	end
	
	for _, data in pairs(self.db.profile.comboPoints.data) do
		Avion2Core:LoadAnimationTables(data.animationList)
	end
end


-------------------------------------------------------------------------------
-- Addon - Combo-Points - Options
-------------------------------------------------------------------------------
-- Fetch animationTable from animationID (as suplied by eg. Menu_Reuqest)
function Avion2_ComboPoints:GetAnimationTable(animationID)
	success, _, arg1, arg2, arg3, arg4 = self:Deserialize(animationID)
	
	if (success) then
		if (arg1 == "energy") then
			return self.db.profile.energy.data[arg2].animationList[arg3]
		elseif (arg1 == "comboPoints") then
			return self.db.profile.comboPoints.data[arg2].animationList[arg3]
		end
	else
		return false
	end
end


-- Build Optionstable
function Avion2_ComboPoints:UpdateOptionTable(args)
	-- Update options when enabled and initialized
	if (self.db and self:IsEnabled()) then
		-- Final options (ENERGY HERE)
		args[self.major.."_Energy"] = {
			type = 'group',
			name = L["EnergyThresholds"],
			desc = L["EnergyThresholds_Desc"],
			args = {
				new = {
					order = 1,
					type = 'input',
					name = L["NewEnergyThreshold"],
					desc = L["NewEnergyThreshold_Desc"],
					usage = L["NewEnergyThreshold_Usage"],
					pattern = "(%d)",
					get = function()
						return ""
					end,
					set = function(info, val)
						if (not self.db.profile.energy.data[tonumber(val)]) then
							self.db.profile.energy.data[tonumber(val)] = {
								animationList 	= {},
								cancelAnimation	= 0,
							}
							
							self:UpdateEventRegistration()
							Avion2Core:UpdateModuleOptionTable(self)
						end
					end,
				},
				onlyOneActive = {
					order = 2,
					type = 'toggle',
					name = L["OnlyOneActive"],
					desc = L["OnlyOneActive_Desc"],
					get = function()
						return self.db.profile.energy.onlyOneActive
					end,
					set = function()
						self.db.profile.energy.onlyOneActive = not self.db.profile.energy.onlyOneActive
					end,
				},
			},
			icon = self:GetIconPath() .. "Energy"
		}		
		-- Options for each energy threshold
		for threshold, data in pairs(self.db.profile.energy.data) do
			args[self.major.."_Energy"].args[string.format("Threshold_%d", threshold)] = {
				type = 'group',
				name = string.format(L["EnergyThreshold"], threshold),
				desc = L["EnergyThreshold_Desc"],
				args = { 
					new = Avion2Core:GetDefaultNewAnimationOption(data.animationList),
					delete = {
						order = 2,
						type = 'execute',
						name = L["DeleteThreshold"],
						desc = L["DeleteThreshold_Desc"],
						func = function()
							Avion2Core:StopAnimationList(string.format("Avion2_ComboPointsThreshold_%d", threshold), data.animationList, false)
							self.db.profile.energy.data[threshold] = nil
							
							self:UpdateEventRegistration()
							Avion2Core:UpdateModuleOptionTable(self)
						end,
					},
					cancelAnimation = {
						order = 3,
						type = 'select',
						name = coreL["CancelAnimation"],
						desc = coreL["CancelAnimation_Desc"],
						values = {
							[0] = coreL["DontCancel"],
							[1] = coreL["FadeOut"],
							[2] = coreL["Cancel"],
						},
						get = function()
							return data.cancelAnimation
						end,
						set = function(info, val)
							data.cancelAnimation = val
						end,
					},
				},
			}
			-- Add options for each animation
			for animationListID, _ in pairs(data.animationList) do
				-- Combine all info to find this animation in module db in ONE string
				local serializedInfo = self:Serialize(Avion2_ComboPoints.major, "energy", threshold, animationListID)
				
				-- Update option table
				args[self.major.."_Energy"].args[string.format("Threshold_%d", threshold)].args[string.format("Animation_%d", animationListID)] = Avion2Core:GetDefaultAnimationOption(data.animationList, animationListID, serializedInfo)
			end
		end	

		-- Final options (COMBOPOINTS HERE)
		args[self.major.."_ComboPoints"] = {
			type = 'group',
			name = L["ComboPointsThresholds"],
			desc = L["ComboPointsThresholds_Desc"],
			args = {
				new = {
					order = 1,
					type = 'input',
					name = L["NewComboThreshold"],
					desc = L["NewComboPointsThreshold_Desc"],
					usage = L["NewComboPointsThreshold_Usage"],
					pattern = "(%d)",
					get = function()
						return ""
					end,
					set = function(info, val)
						if (not self.db.profile.comboPoints.data[tonumber(val)]) then
							self.db.profile.comboPoints.data[tonumber(val)] = {
								animationList 	= {},
								cancelAnimation	= 0,
							}
							
							self:UpdateEventRegistration()
							Avion2Core:UpdateModuleOptionTable(self)
						end
					end,
				},
				onlyOneActive = {
					order = 2,
					type = 'toggle',
					name = L["OnlyOneActive"],
					desc = L["OnlyOneActive_Desc"],
					get = function()
						return self.db.profile.comboPoints.onlyOneActive
					end,
					set = function()
						self.db.profile.comboPoints.onlyOneActive = not self.db.profile.comboPoints.onlyOneActive
					end,
				},
			},
			icon = self:GetIconPath() .. "Combo"
		}		
		-- Options for each compo-point
		for threshold, data in pairs(self.db.profile.comboPoints.data) do
			args[self.major.."_ComboPoints"].args[string.format("Threshold_%d", threshold)] = {
				type = 'group',
				name = string.format(L["ComboThreshold"], threshold),
				desc = L["ComboPointsThreshold_Desc"],
				args = { 
					new = Avion2Core:GetDefaultNewAnimationOption(data.animationList),
					delete = {
						order = 2,
						type = 'execute',
						name = L["DeleteThreshold"],
						desc = L["DeleteThreshold_Desc"],
						func = function()
							Avion2Core:StopAnimationList(string.format("Avion2_ComboPointThreshold_%d", threshold), data.animationList, false)
							self.db.profile.comboPoints.data[threshold] = nil
							
							self:UpdateEventRegistration()
							Avion2Core:UpdateModuleOptionTable(self)
						end,
					},
					cancelAnimation = {
						order = 3,
						type = 'select',
						name = coreL["CancelAnimation"],
						desc = coreL["CancelAnimation_Desc"],
						values = {
							[0] = coreL["DontCancel"],
							[1] = coreL["FadeOut"],
							[2] = coreL["Cancel"],
						},
						get = function()
							return data.cancelAnimation
						end,
						set = function(info, val)
							data.cancelAnimation = val
						end,
					},
				},
			}
			-- Add options for each animation
			for animationListID, _ in pairs(data.animationList) do
				-- Combine all info to find this animation in module db in ONE string
				local serializedInfo = self:Serialize(Avion2_ComboPoints.major, "comboPoints", threshold, animationListID)
				
				-- Update option table
				args[self.major.."_ComboPoints"].args[string.format("Threshold_%d", threshold)].args[string.format("Animation_%d", animationListID)] = Avion2Core:GetDefaultAnimationOption(data.animationList, animationListID, serializedInfo)
			end
		end	
		
	-- Disabled, remove options
	else
		args[self.major .. "_Energy"] 		= nil
		args[self.major .. "_ComboPoints"] 	= nil
	end
end


-------------------------------------------------------------------------------
-- Addon - Combo-Points - Events
-------------------------------------------------------------------------------
-- Energy changed
UpdateEnergy = function()
	-- Update only for player events (that have energy)
	if (unitID == "player" and UnitPowerType("player") == 3) then
		-- Get current amount of energy
		local unitPower = UnitPower("player")
		
		-- Save fitting triggers
		local cache = {}
		
		-- Test for each db entry if it fits
		for threshold, data in pairs(Avion2_ComboPoints.db.profile.energy.data) do
			-- Fitting threshold, add it to list
			if (Avion2_ComboPoints.lastUnitPower < threshold and threshold <= unitPower) then
				table.insert(cache, threshold, true)
			-- Or add to stop-list
			else
				table.insert(cache, threshold, false)
			end
		end
		
		-- Refilter triggers
		if (Avion2_ComboPoints.db.profile.energy.onlyOneActive) then
			-- Filter only highest match
			local bestFit = -1
			for threshold, status in pairs(cache) do
				-- Filter only active triggers
				if (status) then
					-- There is allready a better match, "delete"
					if (threshold < bestFit) then
						cache[threshold] 	= false
					-- Found a better match, "delete" best last match
					elseif (threshold > bestFit) then
						cache[bestFit] 		= false
						bestFit 			= threshold
					end
				end
			end
		end
	
		-- Finally start/stop animations
		for threshold, status in pairs(cache) do
			if (status) then
				Avion2Core:TriggerAnimationList(string.format("Avion2_EnergyThreshold_%d", threshold), Avion2_ComboPoints.db.profile.energy.data[threshold].animationList)
			elseif (Avion2_ComboPoints.db.profile.energy.data[threshold].cancelAnimation ~= 0) then
				Avion2Core:StopAnimationList(   string.format("Avion2_EnergyThreshold_%d", threshold), Avion2_ComboPoints.db.profile.energy.data[threshold].animationList, (Avion2_ComboPoints.db.profile.energy.data[threshold].cancelAnimation == 1))
			end
		end
	
		-- Update last unit power so we don't tigger twice
		Avion2_Energy.lastUnitPower = unitPower
	end
end


-- Energy changed
UpdateComboPoints = function()
	if (unitID == "player" and UnitPowerType("player") == 3) then
		-- Get current amount of combo points
		local comboPoints = GetComboPoints()
		
		-- Save fitting triggers
		local cache = {}
		
		-- Test for each db entry if it fits
		for threshold, data in pairs(Avion2_ComboPoints.db.profile.comboPoints.data) do
			-- Fitting threshold, add it to list
			if (Avion2_ComboPoints.lastComboPoints < threshold and threshold <= comboPoints) then
				table.insert(cache, threshold, true)
			-- Or add to stop-list
			else
				table.insert(cache, threshold, false)
			end
		end
		
		-- Refilter triggers
		if (Avion2_ComboPoints.db.profile.comboPoints.onlyOneActive) then
			-- Filter only highest match
			local bestFit = -1
			for threshold, status in pairs(cache) do
				-- Filter only active triggers
				if (status) then
					-- There is allready a better match, "delete"
					if (threshold < bestFit) then
						cache[threshold] 	= false
					-- Found a better match, "delete" best last match
					elseif (threshold > bestFit) then
						cache[bestFit] 		= false
						bestFit 			= threshold
					end
				end
			end
		end
	
		-- Finally start/stop animations
		for threshold, status in pairs(cache) do
			if (status) then
				Avion2Core:TriggerAnimationList(string.format("Avion2_EnergyThreshold_%d", threshold), Avion2_ComboPoints.db.profile.comboPoints.data[threshold].animationList)
			elseif (Avion2_ComboPoints.db.profile.comboPoints.data[threshold].cancelAnimation ~= 0) then
				Avion2Core:StopAnimationList(   string.format("Avion2_EnergyThreshold_%d", threshold), Avion2_ComboPoints.db.profile.comboPoints.data[threshold].animationList, (Avion2_ComboPoints.db.profile.comboPoints.data[threshold].cancelAnimation == 1))
			end
		end
	
		-- Update last unit power so we don't tigger twice
		Avion2_Energy.lastComboPoints = comboPoints
	end
end
